/*
 * 由Solar Designer编写并置于公共领域。
 * 有关更多信息，请参见crypt_blowfish.c。
 *
 * contrib/pgcrypto/crypt-gensalt.c
 *
 * 此文件包含传统和其他常见crypt(3)算法的盐生成函数，除了bcrypt，它完全定义在crypt_blowfish.c中。
 *
 * 将bcrypt生成器也放在这里，因为crypt-blowfish.c可能并不总是被编译。 -- marko
 */

#include "postgres.h"

#include "px-crypt.h"

typedef unsigned int BF_word;

static unsigned char _crypt_itoa64[64 + 1] =
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

char * _crypt_gensalt_traditional_rn(unsigned long fc_count,
							  const char *fc_input, int fc_size, char *fc_output, int fc_output_size)
{
	if (fc_size < 2 || fc_output_size < 2 + 1 || (fc_count && fc_count != 25))
	{
		if (fc_output_size > 0)
			fc_output[0] = '\0';
		return NULL;
	}

	fc_output[0] = _crypt_itoa64[(unsigned int) fc_input[0] & 0x3f];
	fc_output[1] = _crypt_itoa64[(unsigned int) fc_input[1] & 0x3f];
	fc_output[2] = '\0';

	return fc_output;
}

char * _crypt_gensalt_extended_rn(unsigned long fc_count,
						   const char *fc_input, int fc_size, char *fc_output, int fc_output_size)
{
	unsigned long fc_value;

/* 即使的迭代计数使得从哈希中检测弱DES密钥变得更容易，因此应该避免使用 */
	if (fc_size < 3 || fc_output_size < 1 + 4 + 4 + 1 ||
		(fc_count && (fc_count > 0xffffff || !(fc_count & 1))))
	{
		if (fc_output_size > 0)
			fc_output[0] = '\0';
		return NULL;
	}

	if (!fc_count)
		fc_count = 725;

	fc_output[0] = '_';
	fc_output[1] = _crypt_itoa64[fc_count & 0x3f];
	fc_output[2] = _crypt_itoa64[(fc_count >> 6) & 0x3f];
	fc_output[3] = _crypt_itoa64[(fc_count >> 12) & 0x3f];
	fc_output[4] = _crypt_itoa64[(fc_count >> 18) & 0x3f];
	fc_value = (unsigned long) (unsigned char) fc_input[0] |
		((unsigned long) (unsigned char) fc_input[1] << 8) |
		((unsigned long) (unsigned char) fc_input[2] << 16);
	fc_output[5] = _crypt_itoa64[fc_value & 0x3f];
	fc_output[6] = _crypt_itoa64[(fc_value >> 6) & 0x3f];
	fc_output[7] = _crypt_itoa64[(fc_value >> 12) & 0x3f];
	fc_output[8] = _crypt_itoa64[(fc_value >> 18) & 0x3f];
	fc_output[9] = '\0';

	return fc_output;
}

char * _crypt_gensalt_md5_rn(unsigned long fc_count,
					  const char *fc_input, int fc_size, char *fc_output, int fc_output_size)
{
	unsigned long fc_value;

	if (fc_size < 3 || fc_output_size < 3 + 4 + 1 || (fc_count && fc_count != 1000))
	{
		if (fc_output_size > 0)
			fc_output[0] = '\0';
		return NULL;
	}

	fc_output[0] = '$';
	fc_output[1] = '1';
	fc_output[2] = '$';
	fc_value = (unsigned long) (unsigned char) fc_input[0] |
		((unsigned long) (unsigned char) fc_input[1] << 8) |
		((unsigned long) (unsigned char) fc_input[2] << 16);
	fc_output[3] = _crypt_itoa64[fc_value & 0x3f];
	fc_output[4] = _crypt_itoa64[(fc_value >> 6) & 0x3f];
	fc_output[5] = _crypt_itoa64[(fc_value >> 12) & 0x3f];
	fc_output[6] = _crypt_itoa64[(fc_value >> 18) & 0x3f];
	fc_output[7] = '\0';

	if (fc_size >= 6 && fc_output_size >= 3 + 4 + 4 + 1)
	{
		fc_value = (unsigned long) (unsigned char) fc_input[3] |
			((unsigned long) (unsigned char) fc_input[4] << 8) |
			((unsigned long) (unsigned char) fc_input[5] << 16);
		fc_output[7] = _crypt_itoa64[fc_value & 0x3f];
		fc_output[8] = _crypt_itoa64[(fc_value >> 6) & 0x3f];
		fc_output[9] = _crypt_itoa64[(fc_value >> 12) & 0x3f];
		fc_output[10] = _crypt_itoa64[(fc_value >> 18) & 0x3f];
		fc_output[11] = '\0';
	}

	return fc_output;
}



static unsigned char BF_itoa64[64 + 1] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

static void fc_BF_encode(char *fc_dst, const BF_word *fc_src, int fc_size)
{
	const unsigned char *fc_sptr = (const unsigned char *) fc_src;
	const unsigned char *fc_end = fc_sptr + fc_size;
	unsigned char *fc_dptr = (unsigned char *) fc_dst;
	unsigned int fc_c1,
				fc_c2;

	do
	{
		fc_c1 = *fc_sptr++;
		*fc_dptr++ = BF_itoa64[fc_c1 >> 2];
		fc_c1 = (fc_c1 & 0x03) << 4;
		if (fc_sptr >= fc_end)
		{
			*fc_dptr++ = BF_itoa64[fc_c1];
			break;
		}

		fc_c2 = *fc_sptr++;
		fc_c1 |= fc_c2 >> 4;
		*fc_dptr++ = BF_itoa64[fc_c1];
		fc_c1 = (fc_c2 & 0x0f) << 2;
		if (fc_sptr >= fc_end)
		{
			*fc_dptr++ = BF_itoa64[fc_c1];
			break;
		}

		fc_c2 = *fc_sptr++;
		fc_c1 |= fc_c2 >> 6;
		*fc_dptr++ = BF_itoa64[fc_c1];
		*fc_dptr++ = BF_itoa64[fc_c2 & 0x3f];
	} while (fc_sptr < fc_end);
}

char * _crypt_gensalt_blowfish_rn(unsigned long fc_count,
						   const char *fc_input, int fc_size, char *fc_output, int fc_output_size)
{
	if (fc_size < 16 || fc_output_size < 7 + 22 + 1 ||
		(fc_count && (fc_count < 4 || fc_count > 31)))
	{
		if (fc_output_size > 0)
			fc_output[0] = '\0';
		return NULL;
	}

	if (!fc_count)
		fc_count = 5;

	fc_output[0] = '$';
	fc_output[1] = '2';
	fc_output[2] = 'a';
	fc_output[3] = '$';
	fc_output[4] = '0' + fc_count / 10;
	fc_output[5] = '0' + fc_count % 10;
	fc_output[6] = '$';

	fc_BF_encode(&fc_output[7], (const BF_word *) fc_input, 16);
	fc_output[7 + 22] = '\0';

	return fc_output;
}
